#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import json

from common.const import CopyDataTypeEnum
from common.parse_parafile import ParamFileUtil
from generaldb.gbase8a.common.const import JsonConstant
from generaldb.gbase8a.util.gbase_util import GbaseUtil


class RestoreParam:

    def __init__(self, param_dict):
        self.param_dict = param_dict
        self.job = {}
        self.copies = []
        self.extend_info = {}
        self.job_id = ""
        self.job_param = {}
        self.target_env = {}
        self.target_obj = {}
        self._parse_param_dict()

    @staticmethod
    def get_node_map(cluster_info, nodes_map):
        if not nodes_map:
            return {}
        cluster_nodes = cluster_info.get(JsonConstant.NODES, [])
        cluster_node_map = {}
        for cluster_node in cluster_nodes:
            is_coordinator = 1 if cluster_node.get(JsonConstant.COORDINATOR_NAME) else 0
            is_data_node = 1 if cluster_node.get(JsonConstant.DATA_NODE_NAME) else 0
            cluster_node_map[cluster_node.get(JsonConstant.IP_ADDRESS)] = is_coordinator - is_data_node
        if not cluster_node_map:
            return {}
        type_map = dict()
        for ip, node_type in cluster_node_map.items():
            agent_id = nodes_map.get(ip)
            type_map[agent_id] = node_type
        return type_map

    @staticmethod
    def is_archive_copy(copy):
        copy_type = copy.get(JsonConstant.TYPE)
        return copy_type in [CopyDataTypeEnum.S3_ARCHIVE, CopyDataTypeEnum.TAP_ARCHIVE]

    @staticmethod
    def get_cluster_info_from_copy(copy):
        cluster_json_str = copy.get(JsonConstant.PROTECT_ENV, {}) \
            .get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.CLUSTER_INFO, '')
        if not cluster_json_str:
            return {}
        try:
            return json.loads(cluster_json_str)
        except Exception:
            return {}

    @staticmethod
    def trans_nodes_to_map(nodes: list):
        nodes_map = {}
        if not nodes:
            return nodes_map
        route_ip = None
        for node in nodes:
            node_id = node.get("id", "")
            agent_ip_list = node.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.AGENT_IP_LIST, "")
            for not_named_ip in agent_ip_list.strip().split(","):
                if not_named_ip not in nodes_map:
                    nodes_map[not_named_ip] = node_id
                else:
                    route_ip = not_named_ip
        if route_ip:
            nodes_map.pop(route_ip)
        return nodes_map

    def get_all_repositories(self):
        return self.copies[-1].get(JsonConstant.REPOSITORIES, []) if self.copies else []

    def get_all_nodes(self):
        if not self.target_env:
            self.target_env = self._get_param_field(JsonConstant.TARGET_ENV, self.job)
        return self.target_env.get(JsonConstant.NODES, [])

    def get_target_env_extend_info(self):
        if not self.target_env:
            self.target_env = self._get_param_field(JsonConstant.TARGET_ENV, self.job)
        extend_info = self.target_env.get(JsonConstant.EXTEND_INFO, {})
        return extend_info

    def get_cluster_info(self):
        cluster_info = {}
        extend_info = self.get_target_env_extend_info()
        if not extend_info:
            return cluster_info
        cluster_info_json_str = extend_info.get(JsonConstant.CLUSTER_INFO)
        if not cluster_info_json_str:
            return cluster_info
        try:
            cluster_info = json.loads(cluster_info_json_str)
        except Exception:
            return cluster_info
        return cluster_info

    def get_target_db_name(self):
        if not self.target_env:
            self._get_param_field(JsonConstant.TARGET_ENV, self.job)
        db_name = self.target_env.get(JsonConstant.NAME, "")
        return db_name.lower()

    def get_target_vcname(self):
        extend_info = self.get_target_env_extend_info()
        vc_name = ""
        if not extend_info:
            return vc_name
        custom_params = extend_info.get(JsonConstant.CUSTOM_PARAMS, "")
        custom_parm_dict = GbaseUtil.parse_custom_str_to_dict(custom_params)
        return custom_parm_dict.get(JsonConstant.VC_NAME, vc_name)

    def parse_target_env_extend_nodes(self):
        cluster_info = self.get_cluster_info()
        all_nodes = cluster_info.get("nodes", [])
        nodes_info = {}
        if not all_nodes:
            return nodes_info
        for node in all_nodes:
            node_ip = node.get("ip_address")
            if node_ip not in nodes_info:
                nodes_info[node_ip] = node
        return nodes_info

    def parse_target_env_nodes(self) -> dict:
        """解析节点id与节点ipList映射关系
        :return: {ip:agent_id}
        """
        all_nodes = self.get_all_nodes()
        return RestoreParam.trans_nodes_to_map(all_nodes)

    def get_local_agent_id(self):
        nodes_map = self.parse_target_env_nodes()
        local_hosts = GbaseUtil.extract_ip()
        for host in local_hosts:
            agent_id = nodes_map.get(host)
            if agent_id:
                return agent_id
        return ""

    def get_all_target_agent_id(self):
        nodes_map = self.parse_target_env_nodes()
        return list(set(nodes_map.values()))

    def get_restore_path_map(self):
        repositories = self.get_all_repositories()
        if not repositories:
            return {}
        restore_path_map = ParamFileUtil.parse_backup_path(repositories)
        return restore_path_map

    def get_data_path(self):
        restore_path_map = self.get_restore_path_map()
        return restore_path_map.get("data_repository", "")

    def get_sub_job_name(self):
        return self.param_dict.get(JsonConstant.SUB_JOB, {}).get(JsonConstant.JOB_NAME)

    def get_param_dict(self):
        return self.param_dict

    def get_recover_copy(self):
        if not self.copies:
            return {}
        return self.copies[-1]

    def get_recover_db_name(self):
        recover_copy = self.get_recover_copy()
        db_name = recover_copy.get(JsonConstant.PROTECT_OBJECT, {}).get(JsonConstant.NAME, "")
        return db_name

    def get_recover_copy_id(self):
        recover_copy = self.get_recover_copy()
        if RestoreParam.is_archive_copy(recover_copy):
            copy_id_list = recover_copy.get(JsonConstant.EXTEND_INFO, {})\
                .get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.COPY_ID_LIST, [])
            if not copy_id_list:
                return ''
            return copy_id_list[-1]
        return recover_copy.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.COPY_ID)

    def get_recover_cycle_point(self):
        recover_copy = self.get_recover_copy()
        recover_extend = recover_copy.get(JsonConstant.EXTEND_INFO)
        # 归档副本嵌套多一层
        if RestoreParam.is_archive_copy(recover_copy):
            recover_extend = recover_extend.get(JsonConstant.EXTEND_INFO)
        if not recover_extend:
            return "", ""
        cycle_ids = recover_extend.get(JsonConstant.CYCLE, "0")
        point_ids = recover_extend.get(JsonConstant.POINT, "")
        return cycle_ids, point_ids

    def get_src_db_version(self):
        pass

    def get_copy_id_list(self):
        recover_copy = self.get_recover_copy()
        if RestoreParam.is_archive_copy(recover_copy):
            return recover_copy.get(JsonConstant.EXTEND_INFO, {})\
                .get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.COPY_ID_LIST, [])
        return recover_copy.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.COPY_ID_LIST, [])

    def get_recover_type(self):
        recover_copy = self.get_recover_copy()
        return recover_copy.get(JsonConstant.TYPE, '')

    def get_source_node_map(self) -> dict:
        copy = self.get_recover_copy()
        cluster_info = RestoreParam.get_cluster_info_from_copy(copy)
        if not cluster_info:
            return {}
        nodes = copy.get(JsonConstant.PROTECT_ENV, {}).get(JsonConstant.NODES, [])
        nodes_map = RestoreParam.trans_nodes_to_map(nodes)
        if not nodes_map:
            return {}
        return RestoreParam.get_node_map(cluster_info, nodes_map)

    def get_target_node_map(self) -> dict:
        cluster_info = self.get_cluster_info()
        if not cluster_info:
            return {}
        nodes_map = self.parse_target_env_nodes()
        if not nodes_map:
            return {}
        return RestoreParam.get_node_map(cluster_info, nodes_map)

    def _get_param_field(self, field, src_dict: dict = None):
        if src_dict is None:
            src_dict = self.param_dict
        tar_field = src_dict.get(field) if isinstance(src_dict, dict) else None
        return tar_field

    def _parse_param_dict(self):
        field_map = (
            (JsonConstant.EXTEND_INFO, "extend_info"),
            (JsonConstant.JOB_ID, "job_id"),
            (JsonConstant.JOB_PARAM, "job_param"),
            (JsonConstant.TARGET_ENV, "target_env"),
            (JsonConstant.TARGET_OBJECT, "target_obj"),
            (JsonConstant.COPIES, "copies")
        )
        self.job = self._get_param_field(JsonConstant.JOB)
        if not self.job:
            return
        for field, attr in field_map:
            val = self._get_param_field(field, self.job)
            setattr(self, attr, val)
